﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kokomo.Mirror.Ia32
{
	public class Address32Reference : Operand
	{
		public Address32Reference(
			AddressingForm32 addressingForm,
			int operandSizeBytes,
			Register segmentOverride = null,
			int displacement = 0,
			int scale = 0,
			Index index = 0,
			Base @base = 0
			)
		{
			switch (operandSizeBytes)
			{
				case 1:
				case 2:
				case 4:
				case 8:
				case 6:
					break;
				default:
					throw new ArgumentException("Invalid operand size");
			}

			this.AddressingForm = addressingForm;
			this.OperandSizeBytes = operandSizeBytes;
			this.Displacement = displacement;
			this.SegmentOverride = segmentOverride;
			this.Scale = scale;
			this.Index = index;
			this.Base = @base;
		}

		public override bool Equals(object obj)
		{
			Address32Reference other = obj as Address32Reference;
			if (other == null) return false;

			bool equals =
				(other.AddressingForm == this.AddressingForm)
				&& (other.OperandSizeBytes == this.OperandSizeBytes)
				&& (other.Displacement == this.Displacement)
				&& (other.Scale == this.Scale)
				&& (other.Index == this.Index)
				&& (other.SegmentOverride == this.SegmentOverride)
				;

			return equals;
		}
		public override int GetHashCode()
		{
			int hash =
				(int)this.AddressingForm
				^ this.OperandSizeBytes
				^ this.Displacement
				^ this.Scale
				^ (int)this.Index
				^ (int)this.Base
				^ this.SegmentOverride.GetHashCode()
				;

			return hash;
		}

		public override string ToString()
		{
			string sizeKeyword = this.OperandSizeKeyword;

			StringBuilder textBuilder = new StringBuilder();

			textBuilder.Append(sizeKeyword);
			textBuilder.Append(" ptr ");
			if (this.SegmentOverride != null)
			{
				textBuilder.Append(this.SegmentOverride.Name);
				textBuilder.Append(":");
			}
			textBuilder.Append("[");

			bool hasExplicitDisplacement = (this.Displacement != 0);

			if (this.AddressingForm == AddressingForm32.disp32)
			{
				hasExplicitDisplacement = false;
				textBuilder.AppendFormat("0{0:X}h", this.Displacement);
			}
			else if (this.AddressingForm == AddressingForm32.Sib)
			{
				if (this.Base == Ia32.Base.disp)
				{
					textBuilder.AppendFormat("0{0:X}h", this.Displacement);
					hasExplicitDisplacement = false;
				}
				else
				{
					textBuilder.Append(this.Base);
				}

				if (this.Index != Ia32.Index.None)
				{
					textBuilder.Append("+");
					textBuilder.Append(this.Index);
					if (this.Scale > 1)
					{
						textBuilder.Append("*");
						textBuilder.Append(this.Scale);
					}
				}
			}
			else
			{
				textBuilder.Append(this.AddressingForm);
			}

			if (hasExplicitDisplacement)
			{
				int displacement = this.Displacement;
				if (displacement != 0)
				{
					char sign = (displacement < 0) ? '-' : '+';
					displacement = Math.Abs(displacement);
					textBuilder.AppendFormat("{0}0{1:X}h", sign, displacement);
				}
			}

			textBuilder.Append("]");

			return textBuilder.ToString();
		}

		public AddressingForm32 AddressingForm { get; private set; }
		public int OperandSizeBytes { get; private set; }
		public int Displacement { get; private set; }
		public int Scale { get; private set; }
		public Index Index { get; private set; }
		public Base Base { get; private set; }
		public Register SegmentOverride { get; private set; }

		public string OperandSizeKeyword
		{
			get
			{
				switch (this.OperandSizeBytes)
				{
					case 1:
						return "byte";
					case 2:
						return "word";
					case 4:
						return "dword";
					case 8:
						return "qword";
					default:
						return null;
				}
			}
		}
	}
}
